/**
 * Aptana Studio
 * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
 * Please see the license.html included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.js.debug.ui.internal.launchConfigurations;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

import com.aptana.core.util.StringUtil;
import com.aptana.debug.core.DebugOptionsManager;
import com.aptana.js.debug.core.ILaunchConfigurationConstants;
import com.aptana.js.debug.ui.JSDebugUIPlugin;
import com.aptana.js.debug.ui.internal.dialogs.HttpServerPathDialog;

/**
 * @author Max Stepanov
 */
public class HttpServerSettingsTab extends AbstractLaunchConfigurationTab {

	/**
	 * PathElement
	 * 
	 */
	private class PathElement {
		protected boolean enabled;
		protected String serverPath;
		protected String localPath;

		protected PathElement(boolean enabled, String serverPath, String localPath) {
			this.enabled = enabled;
			this.serverPath = serverPath;
			this.localPath = localPath;
		}
	}

	/**
	 * TableLabelProvider
	 * 
	 */
	private class TableLabelProvider extends LabelProvider implements ITableLabelProvider {

		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		public String getColumnText(Object element, int columnIndex) {
			if (element instanceof PathElement) {
				PathElement pathElement = (PathElement) element;
				switch (columnIndex) {
				case 1:
					return pathElement.serverPath;
				case 2:
					return pathElement.localPath;
				default:
				}
			}
			return null;
		}

	}

	private Image image;
	private CheckboxTableViewer fListViewer;
	private Button fAddButton;
	private Button fRemoveButton;
	private Button fEditButton;

	private List<PathElement> elements;

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createControl(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setFont(parent.getFont());
		composite.setLayout(new GridLayout(2, false));
		composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		Label label = new Label(composite, SWT.NONE);
		label.setText(Messages.HttpServerSettingsTab_WebServerPathConfiguration);
		label.setFont(parent.getFont());
		label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));

		fListViewer = CheckboxTableViewer.newCheckList(composite, SWT.CHECK | SWT.BORDER | SWT.MULTI
				| SWT.FULL_SELECTION);

		Table table = fListViewer.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		TableColumn[] columns = new TableColumn[] { new TableColumn(table, SWT.NONE), new TableColumn(table, SWT.NONE),
				new TableColumn(table, SWT.NONE), };
		columns[1].setText(Messages.HttpServerSettingsTab_ServerPath);
		columns[2].setText(Messages.HttpServerSettingsTab_WorkspacePath);

		TableLayout tableLayout = new TableLayout();
		tableLayout.addColumnData(new ColumnPixelData(24));
		tableLayout.addColumnData(new ColumnWeightData(40));
		tableLayout.addColumnData(new ColumnWeightData(60));
		table.setLayout(tableLayout);

		fListViewer.setContentProvider(new ArrayContentProvider());
		fListViewer.setLabelProvider(new TableLabelProvider());

		table.setFont(parent.getFont());
		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		// button container
		Composite buttonContainer = new Composite(composite, SWT.NONE);
		buttonContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		GridLayout buttonLayout = new GridLayout(1, false);
		buttonLayout.marginHeight = 0;
		buttonLayout.marginWidth = 0;
		buttonContainer.setLayout(buttonLayout);

		GridData data;

		// Add type button
		fAddButton = new Button(buttonContainer, SWT.PUSH);
		fAddButton.setText(StringUtil.ellipsify(Messages.HttpServerSettingsTab_Add));
		fAddButton.setToolTipText(Messages.HttpServerSettingsTab_AddServerPath);
		fAddButton.setFont(parent.getFont());
		data = new GridData(SWT.FILL, SWT.DEFAULT);
		data.widthHint = Math.max(
				new PixelConverter(fAddButton).convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH),
				fAddButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		fAddButton.setLayoutData(data);

		// Edit button
		fEditButton = new Button(buttonContainer, SWT.PUSH);
		fEditButton.setText(StringUtil.ellipsify(Messages.HttpServerSettingsTab_Edit));
		fEditButton.setToolTipText(Messages.HttpServerSettingsTab_EditSelectedPath);
		fEditButton.setFont(parent.getFont());
		data = new GridData(SWT.FILL, SWT.DEFAULT);
		data.widthHint = Math.max(
				new PixelConverter(fEditButton).convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH),
				fEditButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		fEditButton.setLayoutData(data);

		// Remove button
		fRemoveButton = new Button(buttonContainer, SWT.PUSH);
		fRemoveButton.setText(Messages.HttpServerSettingsTab_Remove);
		fRemoveButton.setToolTipText(Messages.HttpServerSettingsTab_RemoveSelectedPath);
		fRemoveButton.setFont(parent.getFont());
		data = new GridData(SWT.FILL, SWT.DEFAULT);
		data.widthHint = Math.max(
				new PixelConverter(fRemoveButton).convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH),
				fRemoveButton.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		fRemoveButton.setLayoutData(data);

		fListViewer.addCheckStateListener(new ICheckStateListener() {
			public void checkStateChanged(CheckStateChangedEvent event) {
				((PathElement) event.getElement()).enabled = event.getChecked();
				setDirty(true);
				updateLaunchConfigurationDialog();
			}
		});
		fListViewer.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				updatePage((IStructuredSelection) event.getSelection());
			}
		});
		fListViewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				if (!event.getSelection().isEmpty()) {
					editPath();
				}
			}
		});
		table.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent event) {
				if (event.character == SWT.DEL && event.stateMask == 0) {
					removePaths();
				}
			}
		});

		fAddButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				addPath();
			}
		});

		fEditButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				editPath();
			}
		});
		fEditButton.setEnabled(false);

		fRemoveButton.addListener(SWT.Selection, new Listener() {
			public void handleEvent(Event e) {
				removePaths();
			}
		});
		fRemoveButton.setEnabled(false);

		setControl(composite);
	}

	private void addPath() {
		HttpServerPathDialog dlg = new HttpServerPathDialog(getShell(), Messages.HttpServerSettingsTab_AddNewPath);
		if (dlg.open() == Window.OK) {
			PathElement element = new PathElement(true, dlg.getServerPath(), dlg.getWorkspaceResource().getFullPath()
					.toPortableString());
			elements.add(element);
			fListViewer.refresh();
			fListViewer.setSelection(new StructuredSelection(element));

			refreshViewer();
			setDirty(true);
			updateLaunchConfigurationDialog();
		}
	}

	private void editPath() {
		PathElement element = (PathElement) ((IStructuredSelection) fListViewer.getSelection()).getFirstElement();
		if (element == null) {
			return;
		}
		HttpServerPathDialog dlg = new HttpServerPathDialog(getShell(), Messages.HttpServerSettingsTab_EditPath);
		dlg.setServerPath(element.serverPath);
		dlg.setWorkspaceResource(ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(element.localPath)));
		if (dlg.open() == Window.OK) {
			element.serverPath = dlg.getServerPath();
			element.localPath = dlg.getWorkspaceResource().getFullPath().toPortableString();
			element.enabled = true;

			fListViewer.update(element, null);
			refreshViewer();
			setDirty(true);
			updateLaunchConfigurationDialog();
		}
	}

	private void removePaths() {
		IStructuredSelection selection = (IStructuredSelection) fListViewer.getSelection();
		Object first = selection.getFirstElement();
		int index = -1;
		for (int i = 0; i < elements.size(); i++) {
			Object object = elements.get(i);
			if (object.equals(first)) {
				index = i;
				break;
			}
		}
		elements.removeAll(selection.toList());
		if (index > elements.size() - 1) {
			index = elements.size() - 1;
		}
		if (index >= 0) {
			fListViewer.setSelection(new StructuredSelection(elements.get(index)));
		}
		setDirty(true);
		updateLaunchConfigurationDialog();
	}

	/**
	 * Refresh the formatter list viewer.
	 */
	private void refreshViewer() {
		ArrayList<PathElement> checkedElements = new ArrayList<PathElement>();
		for (PathElement pathElement : elements) {
			if (pathElement.enabled) {
				checkedElements.add(pathElement);
			}
		}
		fListViewer.setAllChecked(false);
		fListViewer.setCheckedElements(checkedElements.toArray(new PathElement[checkedElements.size()]));
	}

	private void updatePage(IStructuredSelection selection) {
		fRemoveButton.setEnabled(!selection.isEmpty());
		fEditButton.setEnabled(selection.size() == 1);
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
	 */
	public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
		//configuration.setAttribute(ILaunchConfigurationConstants.CONFIGURATION_SERVER_PATHS_MAPPING, StringUtil.EMPTY);
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
	 */
	public void initializeFrom(ILaunchConfiguration configuration) {
		try {
			String[] list = DebugOptionsManager.parseList(configuration.getAttribute(
					"UNUSED", StringUtil.EMPTY));
			elements = new ArrayList<PathElement>();
			for (int i = 0, length = list.length; i < length;) {
				String serverPath = list[i++];
				String localPath = list[i++];
				boolean enabled = !"0".equals(list[i++]); //$NON-NLS-1$
				elements.add(new PathElement(enabled, serverPath, localPath));
			}
			fListViewer.setInput(elements);
			refreshViewer();
		} catch (CoreException e) {
			JSDebugUIPlugin.log("Reading launch configuration fails", e); //$NON-NLS-1$
		}
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy)
	 */
	public void performApply(ILaunchConfigurationWorkingCopy configuration) {
		String[] values = new String[elements.size() * 3];
		int index = 0;
		for (PathElement element : elements) {
			values[index++] = element.serverPath;
			values[index++] = element.localPath;
			values[index++] = element.enabled ? "1" : "0"; //$NON-NLS-1$ //$NON-NLS-2$
		}

		//configuration.setAttribute(ILaunchConfigurationConstants.CONFIGURATION_SERVER_PATHS_MAPPING, DebugOptionsManager.serializeList(values));
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName()
	 */
	public String getName() {
		return Messages.HttpServerSettingsTab_Title;
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage()
	 */
	public Image getImage() {
		if (image == null) {
			image = JSDebugUIPlugin.getImageDescriptor("icons/full/obj16/launch-tree.gif").createImage(); //$NON-NLS-1$
		}
		return image;
	}

	/**
	 * @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose()
	 */
	public void dispose() {
		if (image != null) {
			image.dispose();
		}
		super.dispose();
	}
}
